package com.ying.tests.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * redis 加锁的方式 实现高并发小分布式事务的处理
 * @author 小明
 * @date 2020/08/26
 */
@RestController
public class DistributedLockController {

    @Autowired
    private RedisTemplate redisTemplate;

    @GetMapping("/test")
    public String distributedLock() {
        String lockKey = "lockKey";
        String clientId = UUID.randomUUID().toString();
        try {
            // 先加锁
            Boolean flag = redisTemplate.opsForValue().setIfAbsent(lockKey, clientId, 10, TimeUnit.SECONDS);
            // 加锁成功 执行扣除库存
            if (flag) {
                // 执行定时器 判断锁是否存在 存在 则延长失效时间
                timer(lockKey, clientId);
                // 读取库存
                Integer stock = (Integer) redisTemplate.opsForValue().get("stock");
                if (stock != null && stock > 0) {
                    redisTemplate.opsForValue().set("stock", stock - 1);
                    System.out.println("抢购成功--扣除库存成功" + "-----原库存:" + stock + "剩余库存:" + (stock - 1));
                }
            }
        } finally {
            // 释放锁
            String verfiy = (String) redisTemplate.opsForValue().get(lockKey);
            // 判断是否为当前线程加的锁
            if (Objects.equals(verfiy, clientId)) {
                redisTemplate.delete(lockKey);
            }
        }
        return "end";
    }

    /**
     * 5秒钟一次
     * @param lockKey
     * @param clientId
     */
    @Async
    public void timer(String lockKey, String clientId) {
        ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
        // 参数：1、任务体 2、首次执行的延时时间
        //      3、任务执行间隔 4、间隔时间单位
        service.scheduleAtFixedRate(()->{
            String result = (String) redisTemplate.opsForValue().get(lockKey);
            if (result != null && result.equals(clientId)) {
                redisTemplate.expire(lockKey, 10 , TimeUnit.SECONDS);
            } else {
                service.shutdown();
            }
        }, 0, 5, TimeUnit.SECONDS);
    }
}